home *** CD-ROM | disk | FTP | other *** search
/ Clickx 115 / Clickx 115.iso / software / tools / windows / tails-i386-0.16.iso / live / filesystem.squashfs / usr / share / system-config-printer / probe_printer.py < prev    next >
Encoding:
Python Source  |  2010-09-28  |  13.0 KB  |  435 lines

  1. ## system-config-printer
  2.  
  3. ## Copyright (C) 2006, 2007, 2008, 2009, 2010 Red Hat, Inc.
  4. ## Copyright (C) 2006 Florian Festi <ffesti@redhat.com>
  5. ## Copyright (C) 2007, 2008, 2009 Tim Waugh <twaugh@redhat.com>
  6.  
  7. ## This program is free software; you can redistribute it and/or modify
  8. ## it under the terms of the GNU General Public License as published by
  9. ## the Free Software Foundation; either version 2 of the License, or
  10. ## (at your option) any later version.
  11.  
  12. ## This program is distributed in the hope that it will be useful,
  13. ## but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  15. ## GNU General Public License for more details.
  16.  
  17. ## You should have received a copy of the GNU General Public License
  18. ## along with this program; if not, write to the Free Software
  19. ## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  20.  
  21. import cupshelpers
  22. from debug import *
  23. import errno
  24. import socket, time
  25. import gtk
  26. from timedops import TimedOperation
  27. import subprocess
  28. import threading
  29. import errno
  30. import cups
  31.  
  32. try:
  33.     import pysmb
  34.     PYSMB_AVAILABLE=True
  35. except:
  36.     PYSMB_AVAILABLE=False
  37.     class pysmb:
  38.         class AuthContext:
  39.             pass
  40.  
  41. def wordsep (line):
  42.     words = []
  43.     escaped = False
  44.     quoted = False
  45.     in_word = False
  46.     word = ''
  47.     n = len (line)
  48.     for i in range (n):
  49.         ch = line[i]
  50.         if escaped:
  51.             word += ch
  52.             escaped = False
  53.             continue
  54.  
  55.         if ch == '\\':
  56.             in_word = True
  57.             escaped = True
  58.             continue
  59.  
  60.         if in_word:
  61.             if quoted:
  62.                 if ch == '"':
  63.                     quoted = False
  64.                 else:
  65.                     word += ch
  66.             elif ch.isspace ():
  67.                 words.append (word)
  68.                 word = ''
  69.                 in_word = False
  70.             elif ch == '"':
  71.                 quoted = True
  72.             else:
  73.                 word += ch
  74.         else:
  75.             if ch == '"':
  76.                 in_word = True
  77.                 quoted = True
  78.             elif not ch.isspace ():
  79.                 in_word = True
  80.                 word += ch
  81.  
  82.     if word != '':
  83.         words.append (word)
  84.  
  85.     return words
  86.  
  87. ### should be ['network', 'foo bar', ' ofoo', '"', '2 3']
  88. ##print wordsep ('network "foo bar" \ ofoo "\\"" 2" "3')
  89.  
  90. def open_socket(hostname, port):
  91.     try:
  92.         host, port = hostname.split(":", 1)
  93.     except ValueError:
  94.         host = hostname
  95.         
  96.     s = None
  97.     try:
  98.         ai = socket.getaddrinfo(host, port, socket.AF_UNSPEC,
  99.                                 socket.SOCK_STREAM)
  100.     except socket.gaierror:
  101.         ai = []
  102.  
  103.     for res in ai:
  104.         af, socktype, proto, canonname, sa = res
  105.         try:
  106.             s = socket.socket(af, socktype, proto)
  107.             s.settimeout(0.5)
  108.         except socket.error, msg:
  109.             s = None
  110.             continue
  111.         try:
  112.             s.connect(sa)
  113.         except socket.error, msg:
  114.             s.close()
  115.             s = None
  116.             continue
  117.         break
  118.     return s
  119.  
  120. class LpdServer:
  121.     def __init__(self, hostname):
  122.         self.hostname = hostname
  123.         self.max_lpt_com = 8
  124.  
  125.     def probe_queue(self,name, result):
  126.         s = open_socket(self.hostname, 515)
  127.         if not s: return False
  128.         print name
  129.         
  130.         try:
  131.             s.send('\2%s\n' % name) # cmd send job to queue
  132.             data = s.recv(1024) # receive status
  133.             print repr(data)
  134.         except socket.error, msg:
  135.             print msg
  136.             try:
  137.                 s.close ()
  138.             except:
  139.                 pass
  140.  
  141.             return False
  142.  
  143.         if len(data)>0 and ord(data[0])==0:
  144.             try:
  145.                 s.send('\1\n') # abort job again
  146.                 s.close ()
  147.             except:
  148.                 pass
  149.  
  150.             result.append(name)
  151.             return True
  152.  
  153.         try:
  154.             s.close()
  155.         except:
  156.             pass
  157.  
  158.         return False
  159.  
  160.     def get_possible_queue_names (self):
  161.         candidate = ["PASSTHRU", "ps", "lp", "PORT1", ""]
  162.         for nr in range (self.max_lpt_com):
  163.             candidate.extend (["LPT%d" % nr,
  164.                                "LPT%d_PASSTHRU" % nr,
  165.                                "COM%d" % nr,
  166.                                "COM%d_PASSTHRU" % nr])
  167.         for nr in range (50):
  168.             candidate.append ("pr%d" % nr)
  169.  
  170.         return candidate
  171.  
  172.     def probe(self):
  173.         result = []
  174.         for name in self.get_possible_queue_names ():
  175.             while gtk.events_pending ():
  176.                 gtk.main_iteration ()
  177.  
  178.             found = self.probe_queue(name, result)
  179.             if not found and name.startswith ("pr"):
  180.                 break
  181.             time.sleep(0.1) # avoid DOS and following counter measures 
  182.  
  183.         return result
  184.  
  185. class BackgroundSmbAuthContext(pysmb.AuthContext):
  186.     """An SMB AuthContext class that is only ever run from
  187.     a non-GUI thread."""
  188.  
  189.     def __init__ (self, *args, **kwargs):
  190.         self._gui_event = threading.Event ()
  191.         pysmb.AuthContext.__init__ (self, *args, **kwargs)
  192.  
  193.     def _do_perform_authentication (self):
  194.         gtk.gdk.threads_enter ()
  195.         result = pysmb.AuthContext.perform_authentication (self)
  196.         gtk.gdk.threads_leave ()
  197.         self._do_perform_authentication_result = result
  198.         self._gui_event.set ()
  199.         
  200.     def perform_authentication (self):
  201.         if (self.passes == 0 or
  202.             not self.has_failed or
  203.             not self.auth_called or
  204.             (self.auth_called and not self.tried_guest)):
  205.             # Safe to call the base function.  It won't try any UI stuff.
  206.             return pysmb.AuthContext.perform_authentication (self)
  207.  
  208.         self._gui_event.clear ()
  209.         gobject.timeout_add (1, self._do_perform_authentication)
  210.         self._gui_event.wait ()
  211.         return self._do_perform_authentication_result
  212.  
  213. class PrinterFinder:
  214.     def __init__ (self):
  215.         self.quit = False
  216.  
  217.     def find (self, hostname, callback_fn):
  218.         self.hostname = hostname
  219.         self.callback_fn = callback_fn
  220.         self.op = TimedOperation (self._do_find, callback=lambda x, y: None)
  221.  
  222.     def cancel (self):
  223.         self.op.cancel ()
  224.         self.quit = True
  225.  
  226.     def _do_find (self):
  227.         self._cached_attributes = dict()
  228.         for fn in [self._probe_jetdirect,
  229.                    self._probe_ipp,
  230.                    self._probe_snmp,
  231.                    self._probe_lpd,
  232.                    self._probe_hplip,
  233.                    self._probe_smb]:
  234.             if self.quit:
  235.                 return
  236.  
  237.             try:
  238.                 fn ()
  239.             except Exception, e:
  240.                 nonfatalException ()
  241.  
  242.         # Signal that we've finished.
  243.         if not self.quit:
  244.             self.callback_fn (None)
  245.  
  246.     def _new_device (self, uri, info, location = None):
  247.         device_dict = { 'device-class': 'network',
  248.                         'device-info': "%s" % info }
  249.         if location:
  250.             device_dict['device-location']=location
  251.         device_dict.update (self._cached_attributes)
  252.         new_device = cupshelpers.Device (uri, **device_dict)
  253.         debugprint ("Device found: %s" % uri)
  254.         self.callback_fn (new_device)
  255.  
  256.     def _probe_snmp (self):
  257.         # Run the CUPS SNMP backend, pointing it at the host.
  258.         null = file ("/dev/null", "r+")
  259.         try:
  260.             p = subprocess.Popen (args=["/usr/lib/cups/backend/snmp",
  261.                                         self.hostname],
  262.                                   close_fds=True,
  263.                                   stdin=null,
  264.                                   stdout=subprocess.PIPE,
  265.                                   stderr=null)
  266.         except OSError, e:
  267.             if e == errno.ENOENT:
  268.                 return
  269.  
  270.             raise
  271.  
  272.         (stdout, stderr) = p.communicate ()
  273.         if p.returncode != 0:
  274.             return
  275.  
  276.         if self.quit:
  277.             return
  278.  
  279.         for line in stdout.split ('\n'):
  280.             words = wordsep (line)
  281.             n = len (words)
  282.             if n == 6:
  283.                 (device_class, uri, make_and_model,
  284.                  info, device_id, device_location) = words
  285.             elif n == 5:
  286.                 (device_class, uri, make_and_model, info, device_id) = words
  287.             elif n == 4:
  288.                 (device_class, uri, make_and_model, info) = words
  289.             else:
  290.                 continue
  291.  
  292.             device_dict = { 'device-class': device_class,
  293.                             'device-make-and-model': make_and_model,
  294.                             'device-info': info }
  295.             if n == 5:
  296.                 device_dict['device-id'] = device_id
  297.             if n == 6:
  298.                 device_dict['device-location'] = device_location
  299.  
  300.             device = cupshelpers.Device (uri, **device_dict)
  301.             self.callback_fn (device)
  302.  
  303.             # Cache the make and model for use by other search methods
  304.             # that are not able to determine it.
  305.             self._cached_attributes['device-make-and-model'] = make_and_model
  306.  
  307.     def _probe_lpd (self):
  308.         lpd = LpdServer (self.hostname)
  309.         for name in lpd.get_possible_queue_names ():
  310.             if self.quit:
  311.                 return
  312.  
  313.             found = lpd.probe_queue (name, [])
  314.             if found:
  315.                 uri = "lpd://%s/%s" % (self.hostname, name)
  316.                 self._new_device(uri, self.hostname)
  317.  
  318.             if not found and name.startswith ("pr"):
  319.                 break
  320.  
  321.             time.sleep(0.1) # avoid DOS and following counter measures 
  322.  
  323.     def _probe_hplip (self):
  324.         null = file ("/dev/null", "r+")
  325.         try:
  326.             p = subprocess.Popen (args=["hp-makeuri", "-c", self.hostname],
  327.                                   close_fds=True,
  328.                                   stdin=null,
  329.                                   stdout=subprocess.PIPE,
  330.                                   stderr=null)
  331.         except OSError, e:
  332.             if e == errno.ENOENT:
  333.                 return
  334.  
  335.             raise
  336.  
  337.         (stdout, stderr) = p.communicate ()
  338.         if p.returncode != 0:
  339.             return
  340.  
  341.         if self.quit:
  342.             return
  343.  
  344.         uri = stdout.strip ()
  345.         if uri.find (":") != -1:
  346.             self._new_device(uri, uri)
  347.  
  348.     def _probe_smb (self):
  349.         if not PYSMB_AVAILABLE:
  350.             return
  351.  
  352.         smbc_auth = BackgroundSmbAuthContext ()
  353.         debug = 0
  354.         if get_debugging ():
  355.             debug = 10
  356.         ctx = pysmb.smbc.Context (debug=debug,
  357.                                   auth_fn=smbc_auth.callback)
  358.         entries = []
  359.         uri = "smb://%s/" % self.hostname
  360.         try:
  361.             while smbc_auth.perform_authentication () > 0:
  362.                 if self.quit:
  363.                     return
  364.  
  365.                 try:
  366.                     entries = ctx.opendir (uri).getdents ()
  367.                 except Exception, e:
  368.                     smbc_auth.failed (e)
  369.         except RuntimeError, (e, s):
  370.             if e not in [errno.ENOENT, errno.EACCES, errno.EPERM]:
  371.                 debugprint ("Runtime error: %s" % repr ((e, s)))
  372.         except:
  373.             nonfatalException ()
  374.  
  375.         if self.quit:
  376.             return
  377.  
  378.         for entry in entries:
  379.             if entry.smbc_type == pysmb.smbc.PRINTER_SHARE:
  380.                 uri = "smb://%s/%s" % (self.hostname, entry.name)
  381.                 info = "SMB (%s)" % self.hostname
  382.                 self._new_device(uri, info)
  383.  
  384.     def _probe_jetdirect (self):
  385.         port = 9100    #jetdirect
  386.         sock_address = (self.hostname, port)
  387.         s = open_socket(self.hostname, port)
  388.         if not s:
  389.             debugprint ("%s:%d CLOSED" % sock_address)
  390.         else:
  391.             # port is open so assume its a JetDirect device
  392.             debugprint ("%s:%d OPEN" % sock_address)
  393.             uri = "socket://%s:%d" % sock_address
  394.             info = "JetDirect (%s)" % self.hostname
  395.             self._new_device(uri, info)
  396.             s.close ()
  397.  
  398.     def _probe_ipp (self):
  399.         try:
  400.             ai = socket.getaddrinfo(self.hostname, 631, socket.AF_UNSPEC,
  401.                                     socket.SOCK_STREAM)
  402.         except socket.gaierror:
  403.             debugprint ("Can't resolve %s" % self.hostname)
  404.             return
  405.         for res in ai:
  406.             af, socktype, proto, canonname, sa = res
  407.             if (af == socket.AF_INET and sa[0] == '127.0.0.1' or
  408.                 af == socket.AF_INET6 and sa[0] == '::1'):
  409.                 debugprint ("Do not probe local cups server")
  410.                 return
  411.  
  412.         try:
  413.             c = cups.Connection (host = self.hostname)
  414.         except RuntimeError:
  415.             debugprint ("Can't connect to server/printer")
  416.             return
  417.  
  418.         try:
  419.             printers = c.getPrinters ()
  420.         except cups.IPPError:
  421.             debugprint ("%s is probably not a cups server but IPP printer" %
  422.                         self.hostname)
  423.             uri = "ipp://%s:631/ipp" % (self.hostname)
  424.             info = "IPP (%s)" % self.hostname
  425.             self._new_device(uri, info)
  426.             return
  427.  
  428.         for name, queue in printers.iteritems ():
  429.             uri = queue['printer-uri-supported']
  430.             info = queue['printer-info']
  431.             location = queue['printer-location']
  432.             self._new_device(uri, info, location)
  433.  
  434.  
  435.